using System;
using System.Security;
using System.Xml;
using System.Runtime.InteropServices;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Serialization;
using System.DirectoryServices;
using System.DirectoryServices.ActiveDirectory;
using System.Threading;
using System.Security.Principal;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;

using ActiveDs;

using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;

using System.ComponentModel;

namespace NittiTre
{
    [Guid("0b4420e7-b635-4b9d-ae48-5ead912ac4d8")]
    [XmlRoot(Namespace = "NittiTre")]
    
    public class PasswordHandler : System.Web.UI.WebControls.WebParts.WebPart
    {
        Label labelStatus = default(Label);
        LinkButton changePwdNowButton = default(LinkButton);
        HtmlButton submitChangePwdButton = default(HtmlButton);
        HtmlInputPassword oldPwdInput = default(HtmlInputPassword);
        HtmlInputPassword newPwdInput = default(HtmlInputPassword);
        HtmlInputPassword newPwdConfirmInput = default(HtmlInputPassword);
        HtmlTable table = default(HtmlTable);
        viewMode webpartViewMode = default(viewMode);
        DateTime pwdExpiresDate = default(DateTime);
        DateTime pwdChangedDate = default(DateTime);
        DateTime pwdMiniumChangeDate = default(DateTime);
        LinkButton returnToFrontLink = default(LinkButton);
        Boolean canChangePassword = default(Boolean);
        Boolean passwordExpires = default(Boolean);

        #region Customizable properties

        #region Misc properties
        private Boolean m_allowPasswordChange = true;

        [WebBrowsable(true), Personalizable(true), WebDisplayName("Allow password change"), Category("Settings"), DefaultValue(true), WebDescription("Only show password age. Hides 'change password now' link if disabled.")]
        public Boolean allowPasswordChange
        {
            get { return m_allowPasswordChange; }
            set { m_allowPasswordChange = value; }
        }



        #endregion

        #region Text properties
        private string m_changePwdNowButtonText = "Click to change password now";

        [WebBrowsable(true), Personalizable(true), Category("Texts"),
        DefaultValue("Click to change password now"),
       WebPartStorage(Storage.Shared),
       WebDisplayName("Link to change password now"), WebDescription("Text to display on front page link to change password now")]
        public string changePwdNowButtonText
        {
            get { return m_changePwdNowButtonText; }
            set { m_changePwdNowButtonText = value; }
        }


        private string m_passwordsDoNotMatchText = "Passwords do not match";

        [WebBrowsable(true), Personalizable(true), Category("Texts"),
        DefaultValue("Password do not match"),
        WebPartStorage(Storage.Shared),
        WebDisplayName("Password do not match errormessage"), WebDescription("Text to display if new and changed passwords do not match")]
        public string passwordsDoNotMatchText
        {
            get { return m_passwordsDoNotMatchText; }
            set { m_passwordsDoNotMatchText = value; }
        }


        private string m_ChangedPasswordReceipt = "Password changed successfully";

        [WebBrowsable(true), Personalizable(true), Category("Texts"),
        DefaultValue("Password changed successfully"),
        WebPartStorage(Storage.Shared),
        WebDisplayName("Password changed receipt"), WebDescription("Text to display after password has been successfully changed")]
        public string changedPasswordReceipt
        {
            get { return m_ChangedPasswordReceipt; }
            set { m_ChangedPasswordReceipt = value; }
        }


        private string m_submitChangePwdButtonText = "Change password";

        [WebBrowsable(true), Personalizable(true), Category("Texts"),
        DefaultValue("Change password"),
        WebPartStorage(Storage.Shared),
        WebDisplayName("Change password button"), WebDescription("Text on Change password button in New password form")]
        public string submitChangePwdButtonText
        {
            get { return m_submitChangePwdButtonText; }
            set { m_submitChangePwdButtonText = value; }
        }
	

        private string m_confirmNewPwdLabelText = "Confirm new password";

        [WebBrowsable(true), Personalizable(true), Category("Texts"),
        DefaultValue("Confirm new password"),
        WebPartStorage(Storage.Shared),
        WebDisplayName("Confirm new password"), WebDescription("Text to display in front of Confirm new password input box")]
        public string confirmNewPwdLabelText
        {
            get { return m_confirmNewPwdLabelText; }
            set { m_confirmNewPwdLabelText = value; }
        }

        private string m_oldPwdLabelText = "Old password";

        [WebBrowsable(true), Personalizable(true), Category("Texts"),
        DefaultValue("Old password"),
        WebPartStorage(Storage.Shared),
        WebDisplayName("Old password"), WebDescription("Text to display in front of Old password input box")]
        public string oldPwdLabelText
        {
            get { return m_oldPwdLabelText; }
            set { m_oldPwdLabelText = value; }
        }

        private string m_newPwdLabelText = "New password";

        [WebBrowsable(true), Personalizable(true), Category("Texts"),
        DefaultValue("New password"),
        WebPartStorage(Storage.Shared),
        WebDisplayName("New password"), WebDescription("Text to display in front of New password input box")]
        public string newPwdLabelText
        {
            get { return m_newPwdLabelText; }
            set { m_newPwdLabelText = value; }
        }
	
        private string m_returnToFrontLinkText = "Return to front page";

        [WebBrowsable(true), Personalizable(true), Category("Texts"),
        DefaultValue("Return to front page"),
        WebPartStorage(Storage.Shared),
        WebDisplayName("Return to front page"), WebDescription("Text to display on link to return to front page")]
        public string returnToFrontLinkText
        {
            get { return m_returnToFrontLinkText; }
            set { m_returnToFrontLinkText = value; }
        }

        private string m_passworddExpiresText = "Your password will expire in {0} day{2} ({1})";

        [WebBrowsable(true), Personalizable(true), Category("Texts"),
        DefaultValue("Your password will expire in {0} day{2} ({1})"),
        WebPartStorage(Storage.Shared),
        WebDisplayName("Password expires in..."), WebDescription("Text to describe when password will expire. Should have {0}, {1} and {2}")]
        public string passwordExpiresText
        {
            get { return m_passworddExpiresText; }
            set { m_passworddExpiresText = value; }
        }

        private string m_pwdExpiresTextPluralform = "s";

        [WebBrowsable(true), Personalizable(true), Category("Texts"),
        DefaultValue("s"),
        WebPartStorage(Storage.Shared),
        WebDisplayName("Password expires plural ending"), WebDescription("Characters to add after plural days (as in dayS)")]
        public string pwdExpiresTextPluralform
        {
            get { return m_pwdExpiresTextPluralform; }
            set { m_pwdExpiresTextPluralform = value; }
        }


        private string m_pwdExpiresTodayText;

        [WebBrowsable(true), Personalizable(true), Category("Texts"),
        DefaultValue("Your password expires today!"),
        WebPartStorage(Storage.Shared),
        WebDisplayName("Password expires today"), WebDescription("Text to display if password expires today. Shown in red.")]
        public string pwdExpireseTodayText
        {
            get { return m_pwdExpiresTodayText; }
            set { m_pwdExpiresTodayText = value; }
        }

        private string m_nonExpiringPasswordText = "Your password does not expire";

        [WebBrowsable(true), Personalizable(true), Category("Texts"),
        DefaultValue("Your password does not expire!"),
        WebPartStorage(Storage.Shared),
        WebDisplayName("Non-Expiring password"), WebDescription("Text to display if password does not expire. Shown in green.")]
        public string nonExpiringPasswordText
        {
            get { return m_nonExpiringPasswordText; }
            set { m_nonExpiringPasswordText = value; }
        }

        private string m_UnchangablePasswordText = "You cannot change your password";

        [WebBrowsable(true), Personalizable(true), Category("Texts"),
        DefaultValue("Your password can not be changed"),
        WebPartStorage(Storage.Shared),
        WebDisplayName("User cannot change password"), WebDescription("Text to display if password cannot be changed. Link is disabled.")]
        public string unchangablePasswordText
        {
            get { return m_UnchangablePasswordText; }
            set { m_UnchangablePasswordText = value; }
        }

        private string m_MinimumPasswordAgeNotReachedText = "Your password cannot be changed yet";

        [WebBrowsable(true), Personalizable(true), Category("Texts"),
        DefaultValue("Your password can not be changed yet"),
        WebPartStorage(Storage.Shared),
        WebDisplayName("Minimum password age not reached"), WebDescription("Text to display if minimum password age is not reached. Link is disabled.")]
        public string minimumPasswordAgeNotReachedText
        {
            get { return m_MinimumPasswordAgeNotReachedText; }
            set { m_MinimumPasswordAgeNotReachedText = value; }
        }




        #endregion





        #endregion

        public PasswordHandler()
        {
            this.ExportMode = WebPartExportMode.All;
            webpartViewMode = viewMode.initial;

            canChangePassword = true;
            passwordExpires = true;
        }

        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            base.EnsureChildControls();
            table = new HtmlTable();
            labelStatus = new Label();


            DirectoryEntry user = getCurrentADUser();
            DirectoryEntry domain = getUserDomain(user);

            try
            {

                int userAccountControl = (int)user.Properties["userAccountControl"].Value;
                int dontexpireflag = (int)ADS_USER_FLAG.ADS_UF_DONT_EXPIRE_PASSWD;

                if ((userAccountControl & dontexpireflag) == dontexpireflag)
                {
                    passwordExpires = false;
                }

                int cannotChangePassword = (int)ADS_USER_FLAG.ADS_UF_PASSWD_CANT_CHANGE;
                if ((userAccountControl & cannotChangePassword) == cannotChangePassword)
                {
                    canChangePassword = false;
                }
            }
            catch { }
            // Find out if minimum password age has been reached
            try
            {
                LargeInteger minAge = (LargeInteger)domain.Properties["MinPwdAge"].Value;
                

                if ((long)minAge.LowPart == 0) // If minpwdage is not set, passwords can be changed immediately
                {
                    pwdMiniumChangeDate = DateTime.Now.Subtract(new TimeSpan(10)); // Sets minimum time for change to 10 ticks ago
                }
                else
                {
                    long max = (((long)(minAge.HighPart) << 32) + (long)minAge.LowPart);
                    LargeInteger li = user.Properties["pwdLastSet"].Value as LargeInteger;
                    long expDate = (((long)(li.HighPart) << 32) + (long)li.LowPart) - max;

                    pwdMiniumChangeDate = DateTime.FromFileTime(expDate);

                }

            }
            catch { };

            
            

            // Find expiry date of password.
            try
            {
                LargeInteger maxAge = (LargeInteger)domain.Properties["MaxPwdAge"].Value;
                if ((long)maxAge.LowPart == 0) // If maxpwdage is not set, passwords do not expire
                {
                    passwordExpires = false;
                    pwdExpiresDate = DateTime.Now.AddYears(1);
                }
                else
                {
                    long max = (((long)(maxAge.HighPart) << 32) + (long)maxAge.LowPart);
                    LargeInteger li = user.Properties["pwdLastSet"].Value as LargeInteger;
                    long expDate = (((long)(li.HighPart) << 32) + (long)li.LowPart) - max;

                    pwdExpiresDate = DateTime.FromFileTime(expDate);
                }

            }
            catch { };
            // Find when password was last changed.
            try
            {

                pwdChangedDate = dsDatepropToDateTime(user.Properties["pwdLastSet"].Value);
            }
            catch
            {

            }

            // Create interface elements
            
            // Link back to front in case of error
            returnToFrontLink = new LinkButton();
            returnToFrontLink.ID = "returnToFrontLink";
            returnToFrontLink.Text = returnToFrontLinkText;
            returnToFrontLink.Click += new EventHandler(returnToFrontLink_Click);

            // Change password now button
            changePwdNowButton = new LinkButton();
            changePwdNowButton.ID = "changePwdNowButton";

            // Find out if password can be changed.
            TimeSpan daysDiff = pwdMiniumChangeDate.Subtract(DateTime.Now);
            if (daysDiff.Ticks < 0 && canChangePassword) // Minimum password age reached and user can change password
            {
                changePwdNowButton.Click += new EventHandler(changePwdNowButton_ServerClick);
            }

            // Submit new password button
            submitChangePwdButton = new HtmlButton();
            submitChangePwdButton.ID = "submitChangePwdButton";
            submitChangePwdButton.ServerClick += new EventHandler(submitChangePwdButton_ServerClick);

            // Old password input
            oldPwdInput = new HtmlInputPassword();
            oldPwdInput.ID = "oldPwdInput";


            // New password input
            newPwdInput = new HtmlInputPassword();
            newPwdInput.ID = "newPwdInput";

            // New password confirm input
            newPwdConfirmInput = new HtmlInputPassword();
            newPwdConfirmInput.ID = "newPwdConfirmInput";

            // Hide all controls by default
            changePwdNowButton.Visible = false;
            submitChangePwdButton.Visible = false;
            oldPwdInput.Visible = false;
            newPwdConfirmInput.Visible = false;
            newPwdInput.Visible = false;


            //If no attempt at password change is being made
            base.Controls.Add(labelStatus);
            base.Controls.Add(new HtmlGenericControl("br/"));
            
            base.Controls.Add(changePwdNowButton);

            base.Controls.Add(submitChangePwdButton);
            base.Controls.Add(oldPwdInput);
            base.Controls.Add(newPwdInput);
            base.Controls.Add(newPwdConfirmInput);

            base.Controls.Add(returnToFrontLink);

        }

        private void changePwdNowButton_ServerClick(object sender, EventArgs e)
        {
            webpartViewMode = viewMode.changePasswordForm;
            
            HtmlTableRow row = new HtmlTableRow();
            HtmlTableCell cell = new HtmlTableCell();
            
            labelStatus.Visible=false;

            Label oldPwdLabel = new Label();

            oldPwdLabel.Text = oldPwdLabelText;
            cell.Controls.Add(oldPwdLabel);
            row.Cells.Add(cell);
            cell = new HtmlTableCell();
            oldPwdInput.Visible = true;
            cell.Controls.Add(oldPwdInput);
            row.Cells.Add(cell);
            table.Rows.Add(row);

            Label newPwdLabel = new Label();
            row = new HtmlTableRow();
            cell = new HtmlTableCell();
            newPwdLabel.Text = newPwdLabelText;
            cell.Controls.Add(newPwdLabel);
            row.Cells.Add(cell);
            cell = new HtmlTableCell();
            newPwdInput.Visible = true;
            cell.Controls.Add(newPwdInput);
            row.Cells.Add(cell);
            table.Rows.Add(row);

            Label confirmNewPwdLabel = new Label();
            row = new HtmlTableRow();
            cell = new HtmlTableCell();
            confirmNewPwdLabel.Text = confirmNewPwdLabelText;
            cell.Controls.Add(confirmNewPwdLabel);
            row.Cells.Add(cell);
            cell = new HtmlTableCell();
            newPwdConfirmInput.Visible = true;
            cell.Controls.Add(newPwdConfirmInput);
            row.Cells.Add(cell);
            table.Rows.Add(row);

            submitChangePwdButton = new HtmlButton();
            submitChangePwdButton.ServerClick += new EventHandler(submitChangePwdButton_ServerClick);
            submitChangePwdButton.InnerHtml = submitChangePwdButtonText;
            submitChangePwdButton.ID = "submitChangePwdButton";
            submitChangePwdButton.Visible = true;
            row = new HtmlTableRow();
            labelStatus = new Label();
            cell = new HtmlTableCell();
            cell.ColSpan = 2;
            cell.Controls.Add(submitChangePwdButton);
            row.Cells.Add(cell);
            table.Rows.Add(row);

            base.Controls.Add(table); return;
            

        }

        private void submitChangePwdButton_ServerClick(object sender, EventArgs e)
        {
            if (newPwdInput.Value.ToString() == newPwdConfirmInput.Value.ToString())
            {
                DirectoryEntry user = getCurrentADUser();
                try
                {
                    IADsUser _user = (IADsUser)user.NativeObject;
                    _user.ChangePassword(oldPwdInput.Value.ToString(), newPwdInput.Value.ToString());
                    _user.SetInfo();

                    labelStatus.Text = changedPasswordReceipt;
                    labelStatus.Visible = true;
                    webpartViewMode = viewMode.passwordChangedReceipt;
                }
                catch (Exception ex)
                {
                    if (ex.InnerException != null)
                    {
                        userError("En feil oppstod: " + ex.InnerException.Message);
                    }
                    else { userError("En feil oppstod. Feilmeldingen er: " + ex.Message); }
                }
            }
            else
            {
                userError(passwordsDoNotMatchText);
            }

        }

        private void returnToFrontLink_Click(object sender, EventArgs e)
        {
            webpartViewMode = viewMode.initial;
        }

        protected override void Render(HtmlTextWriter output)
        {
            if (webpartViewMode == viewMode.initial)
            {
                // Format correct text
                TimeSpan daysDiff = pwdExpiresDate.Subtract(DateTime.Now);

                String s = pwdExpiresTextPluralform;
                String textToShow = "";
                if (daysDiff.Days==1) {
                    s="";
                }

                if (!passwordExpires) {
                    textToShow = nonExpiringPasswordText;
                    labelStatus.Style[HtmlTextWriterStyle.Color] = "green";
                }
                else if (daysDiff.Days == 0)
                {
                    textToShow = pwdExpireseTodayText;
                    labelStatus.Style[HtmlTextWriterStyle.Color] = "red";
                }
                else
                {
                    textToShow = passwordExpiresText;
                    labelStatus.Style[HtmlTextWriterStyle.Color] = "black";
                }

                labelStatus.Text = string.Format(textToShow, daysDiff.Days, pwdExpiresDate.ToLocalTime().ToLongDateString().ToString(), s);
                labelStatus.Visible = true;

                daysDiff = pwdMiniumChangeDate.Subtract(DateTime.Now);
                if (daysDiff.Ticks > 0) // Minimum password age not reached
                {
                    changePwdNowButton.Text = minimumPasswordAgeNotReachedText;
                }
                else if (canChangePassword)
                {
                    changePwdNowButton.Text = changePwdNowButtonText;
                }
                else {
                    changePwdNowButton.Text = unchangablePasswordText;
                }

                if (allowPasswordChange)
                {
                    changePwdNowButton.Visible = true;
                }

                returnToFrontLink.Visible = false;
            }

            base.Render(output);
        }

        #region Helpers
        private void userError(string p)
        {
            webpartViewMode = viewMode.errorForm;
            Label errorText = new Label();
            errorText.Style[HtmlTextWriterStyle.Color] = "red";
            errorText.Text = p;

            base.Controls.Add(new HtmlGenericControl("br/"));
            base.Controls.Add(errorText);


        }

        public enum viewMode
        {
            initial, changePasswordForm, passwordChangedReceipt, errorForm
        }

        #endregion

        #region AD Helpers

        private DateTime dsDatepropToDateTime(object prop)
        {
            LargeInteger plsVal = prop as LargeInteger;

            if (plsVal.LowPart == 0)
            {
                throw new Exception("Value is null");

            }
            else
            {
                long dateValue = (((long)(plsVal.HighPart) << 32) + (long)plsVal.LowPart);

                DateTime date = new DateTime(dateValue);
                date = date.AddYears(1600);
                return date;
            }
        }
        public enum objectClass
        {
            user, group, computer
        }
        public enum returnType
        {
            distinguishedName, ObjectGUID
        }

        public string GetObjectDistinguishedName(objectClass objectCls, returnType returnValue, string objectName, string LdapDomain)   
        {

            string distinguishedName = string.Empty;
            string connectionPrefix = "LDAP://" + LdapDomain;
            try
            {
                    DirectoryEntry entry = new DirectoryEntry(connectionPrefix);
                DirectorySearcher mySearcher = new DirectorySearcher(entry);

                switch (objectCls)
                {
                    case objectClass.user:
                        mySearcher.Filter = "(&(objectClass=user)(|(cn=" + objectName + ")(sAMAccountName=" + objectName + ")))";
                        break;
                    case objectClass.group:
                        mySearcher.Filter = "(&(objectClass=group)(|(cn=" + objectName + ")(dn=" + objectName + ")))";
                        break;
                    case objectClass.computer:
                        mySearcher.Filter = "(&(objectClass=computer)(|(cn=" + objectName + ")(dn=" + objectName + ")))";
                        break;
                }
                SearchResult result = mySearcher.FindOne();

                if (result == null)
                {
                    throw new NullReferenceException
                    ("unable to locate the distinguishedName for the object " +
                    objectName + " in the " + LdapDomain + " domain");
                }
                DirectoryEntry directoryObject = result.GetDirectoryEntry();
                if (returnValue.Equals(returnType.distinguishedName))
                {
                    distinguishedName = "LDAP://" + directoryObject.Properties
                        ["distinguishedName"].Value;
                }
                if (returnValue.Equals(returnType.ObjectGUID))
                {
                    distinguishedName = directoryObject.Guid.ToString();
                }

                entry.Close();
                entry.Dispose();
                mySearcher.Dispose();
            }
            catch { }
            return distinguishedName;
        }

        public static string FriendlyDomainToLdapDomain(string friendlyDomainName)
        {
            string ldapPath = null;
            try
            {
                DirectoryContext objContext = new DirectoryContext(
                    DirectoryContextType.Domain, friendlyDomainName);
                Domain objDomain = Domain.GetDomain(objContext);
                ldapPath = objDomain.Name;
            }
            catch (DirectoryServicesCOMException e)
            {
                ldapPath = e.Message.ToString();
            }
            catch { }
            return ldapPath;
        }

        private DirectoryEntry getUserDomain(DirectoryEntry user)
        {
            WindowsIdentity currentUser = WindowsIdentity.GetCurrent();
            string userDomain = currentUser.Name.ToString().Remove(currentUser.Name.ToString().IndexOf('\\'));

            string domainDn = FriendlyDomainToLdapDomain(userDomain);
            DirectoryEntry domain = new DirectoryEntry("LDAP://" + domainDn);
            return domain;
        }

        private DirectoryEntry getCurrentADUser()
        {
            WindowsIdentity currentUser = WindowsIdentity.GetCurrent();
            string userName = currentUser.Name.ToString().Substring(currentUser.Name.ToString().IndexOf('\\') + 1);
            string userDomain = currentUser.Name.ToString().Remove(currentUser.Name.ToString().IndexOf('\\'));

            string domainDn = FriendlyDomainToLdapDomain(userDomain);
            string userDN = GetObjectDistinguishedName(objectClass.user, returnType.distinguishedName, userName, domainDn);

            DirectoryEntry user = new DirectoryEntry(userDN);

            return user;

        }

        #endregion


    }

}
